package cn.net.xyan.tts.mac;

import static ca.weblite.objc.util.CocoaUtils.dispatch_sync;
import ca.weblite.objc.NSObject;
import ca.weblite.objc.Proxy;
import ca.weblite.objc.annotations.Msg;
import cn.net.xyan.tts.api.ISynthesizer;
import cn.net.xyan.tts.api.WaveFormat;
import org.scijava.nativelib.NativeLibraryUtil;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import java.io.*;

/**
 * Created by zarra on 17/1/26.
 */
public class NSSpeechSynthesizer extends NSObject implements ISynthesizer{

    public static class NSSpeechSynthesizerDelegateProxy extends NSObject {

        NSSpeechSynthesizer parent;
        NSSpeechSynthesizerDelegate delegate;

        public NSSpeechSynthesizerDelegateProxy(NSSpeechSynthesizer p,NSSpeechSynthesizerDelegate d){
            super();
            init("NSObject");
            this.parent = p;
            this.delegate = d;
        }

        @Msg(selector = "speechSynthesizer:didFinishSpeaking:",signature = "v@:@i")
        public void onFinish(Proxy proxy,int i){
            if (delegate!=null)
                delegate.didFinishSpeaking(parent,i);
        }
    }

    public NSSpeechSynthesizer(){
        super("NSObject");
        NativeLibraryUtil.Architecture arch = NativeLibraryUtil.getArchitecture();

        if (arch == NativeLibraryUtil.Architecture.OSX_64) {
            dispatch_sync(new Runnable() {

                @Override
                public void run() {
                    proxy = getClient().sendProxy("NSSpeechSynthesizer", "alloc").sendProxy("init");
                }

            });
        }else{
            throw new UnsupportedOperationException("NSSpeechSynthesizer must run at mac OS!");
        }

    }

    Proxy proxy;
    NSSpeechSynthesizerDelegateProxy delegateProxy;

    byte[] audioData;

    @Override
    public AudioInputStream audioStream() {
        ByteArrayInputStream in = new ByteArrayInputStream(audioData);
        AudioInputStream audioInputStream = null;
        try {
            audioInputStream = AudioSystem.getAudioInputStream(in);
        }catch (Throwable ioE){
            ioE.printStackTrace();
        }

        return audioInputStream;
    }

    @Override
    public int getRate() {
        final Object[] result = new Object[1];
        dispatch_sync(new Runnable() {

            @Override
            public void run() {
                result[0] = proxy.send("rate");
            }

        });

        if (result[0] instanceof Number){
            Number number = (Number) result[0];
            return number.intValue();

        }

        return 0;
    }

    @Override
    public void setRate(int rate) {
        Float f = new Float(rate);
        dispatch_sync(new Runnable() {

            @Override
            public void run() {
                proxy.send("setRate:",f);
            }

        });
    }

    @Override
    public int getVolume() {
        final Object[] result = new Object[1];
        dispatch_sync(new Runnable() {

            @Override
            public void run() {
                result[0] = proxy.send("volume");
            }

        });

        if (result[0] instanceof Number){
            Number number = (Number) result[0];
            return number.intValue();

        }
        return 0;
    }

    @Override
    public void setVolume(int volume) {
        Float f = new Float(volume);
        dispatch_sync(new Runnable() {

            @Override
            public void run() {
                proxy.send("setVolume:",f);
            }

        });
    }

    @Override
    public WaveFormat getWaveFormat() {
        WaveFormat waveFormat = new WaveFormat(22050,1,16);
        return waveFormat;
    }





    public void setDelegate(NSSpeechSynthesizerDelegate delegate){
        NSSpeechSynthesizerDelegateProxy p = new NSSpeechSynthesizerDelegateProxy(this,delegate);
        delegateProxy = p;
        dispatch_sync(new Runnable() {

            @Override
            public void run() {
                proxy.send("setDelegate:",p);
            }

        });


    }

    @Override
    public void speak(String word,Object option){

        try {
            File temp = File.createTempFile("synthesizer", ".aiff");
            temp.deleteOnExit();
            String tempFileName = temp.getAbsolutePath();
            Proxy url = getClient().sendProxy("NSURL","fileURLWithPath:",tempFileName);

            Boolean lock = new Boolean(true);

            setDelegate(new NSSpeechSynthesizerDelegate() {
                @Override
                public void didFinishSpeaking(NSSpeechSynthesizer synthesizer, int i) {
                    synchronized (lock){
                        lock.notifyAll();
                    }


                }
            });

            dispatch_sync(new Runnable() {

                @Override
                public void run() {
                    proxy.send("startSpeakingString:toURL:", word,url);
                }

            });

            synchronized (lock){
                lock.wait(30*1000);
            }

            try {
                int bufferSize = 8192;
                int readN = 0;
                byte[] buffer = new byte[bufferSize];
                FileInputStream fin = new FileInputStream(temp);
                ByteArrayOutputStream bout = new ByteArrayOutputStream();

                do{
                    readN = fin.read(buffer);
                    if (readN>0)
                        bout.write(buffer,0,readN);
                }while (readN >0);

                audioData = bout.toByteArray();
                fin.close();

                temp.delete();

            }catch (Throwable ioE){
                ioE.printStackTrace();
            }
        }catch (Throwable e){
            e.printStackTrace();
        }

    }

}
